Java 迭代接口:Iterator、ListIterator 和 Spliterator

您所在的位置:网站首页 java迭代器iterator 跳出循环 Java 迭代接口:Iterator、ListIterator 和 Spliterator

Java 迭代接口:Iterator、ListIterator 和 Spliterator

#Java 迭代接口:Iterator、ListIterator 和 Spliterator| 来源: 网络整理| 查看: 265

1. 简介

当我们使用 for 或 while 循环来遍历一个集合的元素,Iterator 允许我们不用担心索引位置,甚至让我们不仅仅是遍历一个集合,同时还可以改变它。例如,你如果要删除循环中的元素,那么 for 循环不见得总是可行的。

结合自定义的迭代器,我们可以迭代更为复杂的对象,以及向前和向后移动,并且知晓如何利用其优势也将变得非常清楚。

本文将深入讨论如何使用 Iterator 和 Iterable 接口。

2. Iterator()

Iterator 接口用于迭代集合中的元素(List,Set 或 Map)。它用于逐个检索元素,并在需要时针对每个元素执行操作。

下面是用于遍历集合与执行操作的方法:

.hasNext():如果还没有到达集合的末尾,则返回 true,否则返回 false .next():返回集合中的下一个元素 .remove():从集合中移除迭代器返回的最后一个元素 .forEachRemaining():按顺序为集合中剩下的每个元素执行给定的操作

首先,由于迭代器是用于集合的,让我们做一个简单的包含几个元素的 ArrayList:

List avengers = new ArrayList(); // Now lets add some Avengers to the list avengers.add("Ant-Man"); avengers.add("Black Widow"); avengers.add("Captain America"); avengers.add("Doctor Strange");复制代码

我们可以使用一个简单循环来遍历这个集合:

System.out.println("Simple loop example:\n"); for (int i = 0; i < avengers.size(); i++) { System.out.println(avengers.get(i)); }复制代码

不过,我们想探索迭代器:

System.out.println("\nIterator Example:\n"); // First we make an Iterator by calling // the .iterator() method on the collection Iterator avengersIterator = avengers.iterator(); // And now we use .hasNext() and .next() to go through it while (avengersIterator.hasNext()) { System.out.println(avengersIterator.next()); }复制代码

如果我们想从这个 ArrayList 中删除一个元素,会发生什么?让我们试着使用常规的 for 循环:

System.out.println("Simple loop example:\n"); for (int i = 0; i < avengers.size(); i++) { if (avengers.get(i).equals("Doctor Strange")) { avengers.remove(i); } System.out.println(avengers.get(i)); }复制代码

我们会收到一个讨厌的 IndexOutOfBoundsException:

Simple loop example: Ant-Man Black Widow Captain America Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 3, Size: 3复制代码

这在遍历集合时更改其大小是有意义的,增强 for 循环也一样:

System.out.println("Simple loop example:\n"); for (String avenger : avengers) { if (avenger.equals("Doctor Strange")) { avengers.remove(avenger); } System.out.println(avenger); }复制代码

我们再次收到了另一个异常:

Simple loop example: Ant-Man Black Widow Captain America Doctor Strange Exception in thread "main" java.util.ConcurrentModificationException复制代码

这时迭代器就派上用场了,由它充当中间人,从集合中删除元素,同时确保遍历按计划继续:

Iterator avengersIterator = avengers.iterator(); while (avengersIterator.hasNext()) { String avenger = avengersIterator.next(); // First we must find the element we wish to remove if (avenger.equals("Ant-Man")) { // This will remove "Ant-Man" from the original // collection, in this case a List avengersIterator.remove(); } }复制代码

这是保证在遍历集合时删除元素的安全方法。

并确认该元素是否已被删除:

// We can also use the helper method .forEachRemaining() System.out.println("For Each Remaining Example:\n"); Iterator avengersIteratorForEach = avengers.iterator(); // This will apply System.out::println to all elements in the collection avengersIteratorForEach.forEachRemaining(System.out::println);复制代码

输出如下:

For Each Remaining Example: Black Widow Captain America Doctor Strange复制代码

正如你所看到的,蚁人已经从 复仇者联盟 的名单中删除了。

2.1. ListIterator()

ListIterator 继承自 Iterator 接口。它只在 List 上进行使用,可以双向迭代,这意味着你可以从前到后或从后到前进行迭代。它也没有 current 元素,因为游标总是放在 List 的两个元素之间,所以我们用 .previous() 或 .next() 来访问元素。

Iterator 和 ListIterator 之间有什么区别呢?

首先,Iterator 可以用于 任意集合 —— List、Map、Queue、Set 等。

ListIterator 只能应用于 List,通过添加这个限制,ListIterator 在方法方面可以更加具体,因此,我们引入了许多新方法,他们可以帮助我们在遍历时对其进行修改。

如果你正在处理 List 实现(ArrayList、LinkedList等),那么使用 ListIterator 更为可取一些。

下面是你可能会用到的方法:

.add(E e):向 List 中添加元素。 .remove():从 List 中删除 .next() 或 .previous() 返回的最后一个元素。 .set(E e):使用指定元素来覆盖 List .next() 或 .previous() 返回的最后一个元素。 .hasNext():如果还没有到达 List 的末尾,则返回 true,否则返回 false。 .next():返回 List 中的下一个元素。 .nextIndex():返回下一元素的下标。 .hasPrevious():如果还没有到达 List 的开头,则返回 true,否则返回 false。 .previous():返回 List 的上一个元素。 .previousIndex():返回上一元素的下标。

再次,让我们用一些元素构成一个 ArrayList:

ArrayList defenders = new ArrayList(); defenders.add("Daredevil"); defenders.add("Luke Cage"); defenders.add("Jessica Jones"); defenders.add("Iron Fist");复制代码

让我们用 ListIterator 来遍历 List 并打印其元素:

ListIterator listIterator = defenders.listIterator(); System.out.println("Original contents of our List:\n"); while (listIterator.hasNext()) System.out.print(listIterator.next() + System.lineSeparator()); 复制代码

显然,它的工作方式与 Iterator 相同。输出如下:

Original contents of our List: Daredevil Luke Cage Jessica Jones Iron Fist复制代码

现在,让我们来尝试修改一些元素:

System.out.println("Modified contents of our List:\n"); // Now let's make a ListIterator and modify the elements ListIterator defendersListIterator = defenders.listIterator(); while (defendersListIterator.hasNext()) { Object element = defendersListIterator.next(); defendersListIterator.set("The Mighty Defender: " + element); }复制代码

现在打印 List 的话会得到如下结果:

Modified contents of our List: The Mighty Defender: Daredevil The Mighty Defender: Luke Cage The Mighty Defender: Jessica Jones The Mighty Defender: Iron Fist复制代码

现在,让我们倒着遍历列表,就像我们可以用 ListIterator 做的那样:

System.out.println("Modified List backwards:\n"); while (defendersListIterator.hasPrevious()) { System.out.println(defendersListIterator.previous()); }复制代码

输出如下:

Modified List backwards: The Mighty Defender: Iron Fist The Mighty Defender: Jessica Jones The Mighty Defender: Luke Cage The Mighty Defender: Daredevil复制代码

3. Spliterator()

Spliterator 接口在功能上与 Iterator 相同。你可能永远不需要直接使用 Spliterator,但让我们继续讨论一些用例。

但是,你应首先熟悉 Java Streams 和 Lambda Expressions in Java。

虽然我们将列出 Spliterator 拥有的所有方法,但是 Spliterator 接口的全部工作超出了本文的范畴。我们将通过一个例子讨论 Spliterator 如何使用并行化更有效地遍历我们可以分解的 Stream。

我们在处理 Spliterator 时使用的方法是:

`.characteristics() : 返回该 Spliterator 具有的作为 复制代码

int

值的特征。 这些包括: - `ORDERED` - `DISTINCT` - `SORTED` - `SIZED` - `CONCURRENT` - `IMMUTABLE` - `NONNULL` - `SUBSIZED` - `.estimateSize()`:返回遍历作为 `long` 值遇到的元素数量的估计值,如果无法返回则返回 `long.MAX_VALUE`。 - `.forEachRemaining(E e)`:按顺序对集合中的每个剩余元素执行给定操作。 - `.getComparator()`:如果该 `Spliterator` 的源是由 `Comparator` 排序的,其将返回 `Comparator`。 - `.getExactSizeIfKnown()`:如果大小已知则返回 `.estimateSize()`,否则返回 `-1`。 - `.hasCharacteristics(int characteristics)`:如果这个 `Spliterator` 的 `.characteristics()` 包含所有给定的特征,则返回 `true`。 - `.tryAdvance(E e)`:如果存在剩余元素,则对其执行给定操作,返回 `true`,否则返回 `false`。 - `.trySplit()`:如果这个 `Spliterator` 可以被分区,返回一个 `Spliterator` 覆盖元素,当从这个方法返回时,它将不被这个 `Spliterator` 覆盖。 像往常一样,让我们从一个简单的 `ArrayList` 开始: 复制代码

List mutants = new ArrayList();

mutants.add("Professor X");mutants.add("Magneto");mutants.add("Storm");mutants.add("Jean Grey");mutants.add("Wolverine");mutants.add("Mystique");

现在,我们需要将 `Spliterator` 应用于 `Stream`。值得庆幸的是,由于 Collections 框架,很容易在 `ArrayList` 和 `Stream` 之间进行转换: 复制代码

// Obtain a Stream to the mutants List.Stream mutantStream = mutants.stream();

// Getting Spliterator object on mutantStream.Spliterator mutantList = mutantStream.spliterator();

为了展示其中的一些方法,让我们分别运行下它们: 复制代码

// .estimateSize() methodSystem.out.println("Estimate size: " + mutantList.estimateSize());

// .getExactSizeIfKnown() methodSystem.out.println("nExact size: " + mutantList.getExactSizeIfKnown());

System.out.println("nContent of List:");// .forEachRemaining() methodmutantList.forEachRemaining((n) -> System.out.println(n));

// Obtaining another Stream to the mutant List.Spliterator splitList1 = mutantStream.spliterator();

// .trySplit() methodSpliterator splitList2 = splitList1.trySplit();

// If splitList1 could be split, use splitList2 first.if (splitList2 != null) {System.out.println("nOutput from splitList2:");splitList2.forEachRemaining((n) -> System.out.println(n));}

// Now, use the splitList1System.out.println("nOutput from splitList1:");splitList1.forEachRemaining((n) -> System.out.println(n));

我们将得到输出: 复制代码

Estimate size: 6

Exact size: 6

Content of List:Professor XMagnetoStormJean GreyWolverineMystique

Output from splitList2:Professor XMagnetoStorm

Output from splitList1:Jean GreyWolverineMystique

## 4. Iterable() 如果出于某种原因,我们想要创建一个自定义的 `Iterator` 接口,应该怎么办?你首先要熟悉的是这张图: ![file](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2019/8/27/16cd06ab2be585b5~tplv-t2oaga2asx-image.image) 要创建自定义 `Iterator`,我们需要为 `.hasNext()`、`.next()` 和 `.remove()` 做自定义实现。 在 `Iterator` 接口中,有一个方法,它返回一个集合中元素的迭代器,即 `.iterator()` 方法,还有一个方法为迭代器中的每个元素执行一个操作的方法,即 `.dorEach()` 方法。 例如,假设我们是 Tony Stark,我们需要写个自定义迭代器来列出当前武器库中的每件钢铁侠套装。 首先,让我们创建一个类来获取和设置 suit 数据: 复制代码

public class Suit {

private String codename; private int mark;复制代码 public Suit(String codename, int mark) { this.codename = codename; this.mark = mark; }复制代码 public String getCodename() { return codename; }复制代码 public int getMark() { return mark; }复制代码 public void setCodename (String codename) {this.codename=codename;}复制代码 public void setMark (int mark) {this.mark=mark;}复制代码 public String toString() { return "mark: " + mark + ", codename: " + codename; } } ```复制代码

接下来让我们编写自定义 Iterator:

// Our custom Iterator must implement the Iterable interface public class Armoury implements Iterable { // Notice that we are using our own class as a data type private List list = null; public Armoury() { // Fill the List with data list = new LinkedList(); list.add(new Suit("HOTROD", 22)); list.add(new Suit("SILVER CENTURION", 33)); list.add(new Suit("SOUTHPAW", 34)); list.add(new Suit("HULKBUSTER 2.0", 48)); } public Iterator iterator() { return new CustomIterator(list); } // Here we are writing our custom Iterator // Notice the generic class E since we do not need to specify an exact class public class CustomIterator implements Iterator { // We need an index to know if we have reached the end of the collection int indexPosition = 0; // We will iterate through the collection as a List List internalList; public CustomIterator(List internalList) { this.internalList = internalList; } // Since java indexes elements from 0, we need to check against indexPosition +1 // to see if we have reached the end of the collection public boolean hasNext() { if (internalList.size() >= indexPosition +1) { return true; } return false; } // This is our custom .next() method public E next() { E val = internalList.get(indexPosition); // If for example, we were to put here "indexPosition +=2" we would skip every // second element in a collection. This is a simple example but we could // write very complex code here to filter precisely which elements are // returned. // Something which would be much more tedious to do with a for or while loop indexPosition += 1; return val; } // In this example we do not need a .remove() method, but it can also be // written if required } }复制代码

最后是 main 方法类:

public class IronMan { public static void main(String[] args) { Armoury armoury = new Armoury(); // Instead of manually writing .hasNext() and .next() methods to iterate through // our collection we can simply use the advanced forloop for (Suit s : armoury) { System.out.println(s); } } }复制代码

输出如下:

mark: 22, codename: HOTROD mark: 33, codename: SILVER CENTURION mark: 34, codename: SOUTHPAW mark: 48, codename: HULKBUSTER 2.0复制代码

5. 总结

本文中,我们详细讨论了如何使用 Java 中的迭代器,甚至写了一个定制的迭代器来探索 Iterable 接口的所有新的可能性。

我们还讨论了 Java 是如何利用 Stream 的并行化,使用 Spliterator 接口对集合的遍历进行内部优化。

8月福利准时来袭,关注公众号​后台回复:003即可领取7月翻译集锦哦~​往期福利回复:001,002即可领取!

img



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3